Analysis of Cyber-security Threats
The purpose of this data analysis is to help model and predict
cyber-security threats to help optimize global digital security. As
well, to analyze which defense mechanisms that are optimal in combating
cyber attacks. The dataset contains 10 variables that are potential
significant covariates in predicting potential threats. The variable
country refers to the country where the attack occurred. The other
variables analyze incident resolution time in hours, number of affected
users, financial loss, attack type, target industry, attack source,
security vulnerability type and defense mechanism used. Here is a full
list of variables:
- Country country where the attack occurred
- Year year of the incident
- Attack Type type of cyber-security threat
- Target Industry industry targeted
- Financial Loss estimated financial loss in
millions
- Number of Affected Users number of users impacted
by the attack
- Attack Source origin of the attack
- Security Vulnerability Type type of vulnerability
exploited
- Defense Mechanism Used cyber-security defense
strategy applied
- Incident Resolution Time time taken to fully
resolve event in hours
cyber <- read.csv('https://raw.githubusercontent.com/GabbyK8/Datasets/refs/heads/main/Global_Cybersecurity_Threats_2015-2024.csv')
cyber$Defense.Mechanism.Used <- ifelse(cyber$Defense.Mechanism.Used == "AI-based Detection", "AI", cyber$Defense.Mechanism.Used)
# plot 1 number of affected users by financial loss by vulnerability type
ggplot(cyber, aes(x = cyber$Number.of.Affected.Users, y = cyber$Financial.Loss..in.Million..., color = cyber$Security.Vulnerability.Type)) +
geom_smooth(aes(x = cyber$Number.of.Affected.Users, y = cyber$Financial.Loss..in.Million..., color = cyber$Security.Vulnerability.Type), method = "lm", se = FALSE) +
scale_size(range = c(2, 10)) +
labs(
title = "Plot of Security Vulnerability, Volume Compromised, and Financial Impact",
x = "Volume Compromised",
y = "Financial Impact",
color = "Security Vulernability"
) +
theme_minimal()
# plot 3 Density plot of attacked industries
Year_data <- cyber %>%
filter(Year == 2015)
p1<- ggplot(Year_data, aes(x = Number.of.Affected.Users, fill = Country)) +
geom_density(alpha = 0.6) +
labs(
x = "Number of Affected Users",
y = "Density",
fill="Country",
title = "Density Plot of Attacked Countries in 2015"
) +
theme_minimal()
ggplotly(p1)
Year_data1 <- cyber %>%
filter(Year == 2024)
# plot 4 Density plot of Attacked countries
p2 <- ggplot(Year_data1, aes(x = Number.of.Affected.Users, fill = Country)) +
geom_density(alpha = 0.6) +
labs(
x = "Number of Affected Users",
y = "Density",
fill="Country",
title = "Density Plot of Attacked Countries in 2024"
) +
theme_minimal()
ggplotly(p2)
Each country is given a specific color and the number of affected
users is displayed on the x-axis. The density is displayed on the
y-axis. Density in this plot shows the relative frequency of number of
affected users per attack in each country.In 2015, the cyber attacks
within the USA more frequently affected about 310,000 users, compared to
Russia where attacks more frequently attacked 670,000 users and Germany
where most attacks more frequently affected 790,000 users. In 2024, the
number of affected users in the USA does not seem to be much more dense
in any one number and is more dispersed. Japan had more frequent attacks
that affected 500,000 and Brazil had it’s most frequent at 750,000
users.
ggplot(cyber, aes(x = cyber$Year, y = cyber$Incident.Resolution.Time..in.Hours., color = cyber$Security.Vulnerability.Type)) +
geom_smooth(aes(x = cyber$Year, y = cyber$Incident.Resolution.Time..in.Hours., color = cyber$Security.Vulnerability.Type), method = "lm", se = FALSE) +
scale_size(range = c(2, 10)) +
labs(
title = "Plot of Security Vulnerability, Year of Attack, and Resolution Time",
x = "Year of Attack",
y = "Incident Resolution Time in Hours",
color = "Security Vulernability"
) +
theme_minimal()
#plot of zero-day attack type by target industry
zero_data <- cyber %>%
filter(Security.Vulnerability.Type == "Zero-day")
a <- ggplot(zero_data, aes(x = zero_data$Attack.Type, fill =zero_data$Target.Industry )) +
geom_bar(position = "dodge") +
labs(
x = "Threat Type",
y = "Proportion",
fill = "Target Industry",
title = "Zero-day Incidences by Threat Type vs. Target Industry"
) +
theme_minimal()
ggplotly(a)
Zero-day attacks seem to commonly use Phising within both retail and
IT industries, based on the height and count of the bar in these two
categories. Ransomware is the most minimally used zero-day attack on the
government, with a count of just 12.
col0=c("#4682B4", "#B4464B","#9900FF","#FF66FF", "#009933")
boxplot(Incident.Resolution.Time..in.Hours. ~ Defense.Mechanism.Used,
data=cyber,
xlab="Defense Mechanism",
ylab="Resolution Time (hours)",
col = col0,
main="Incident Resolution Time by Defense Mechanism",
cex.main = 0.9,
col.main = "blue")
The boxplot shows that the average resolution time among attacks
based on defense mechanisms is relatively the same. The only exception
seems to be firewall, which appears to take slightly less resolution
time on average.We can indicate the average by looking the black
horizontal line within each box. They all appear to line up with one
another indicating similar averages.
Skewness
cyber$Country[cyber$Country=="USA"]<-1
cyber$Country<- ifelse(cyber$Country!="USA",0)
cyber$Security.Vulnerability.Type[cyber$Security.Vulnerability.Type=="Zero-day"]<-1
cyber$Security.Vulnerability.Type<-ifelse(cyber$Security.Vulnerability.Type!="Zero-day",0)
cyber$Number.of.Affected.Users<-as.numeric(cyber$Number.of.Affected.Users)
cyber$Year<-as.numeric(as.factor(cyber$Year))
cyber$Incident.Resolution.Time..in.Hours.<-as.numeric(cyber$Incident.Resolution.Time..in.Hours.)
c<-skewness(cyber$Financial.Loss..in.Million...)
d<-skewness(cyber$Number.of.Affected.Users)
e<-skewness(cyber$Year)
Skewness = cbind(Financial.Loss = c,
Affected.Users = d,
Year = e)
pander(Skewness)
| -0.01684 |
-0.02537 |
-0.02749 |
The data is normally skewed and no transformation is necessary.
Linear Regression
Linear regression is explored below:
# Predictors (x) and Response Variable (y)
set.seed(12345)
X <- as.data.frame(cyber[, -5])
y <- cyber$Financial.Loss..in.Million...
train_index <- createDataPartition(y, p = 0.8, list = FALSE)
X_train <- makeX(X[train_index, ])
X_test <- makeX(X[-train_index, ])
y_train <- y[train_index]
y_test <- y[-train_index]
#standardizing the data
preprocess_params <- preProcess(X_train, method = c("center","scale"))
X_train <- predict(preprocess_params, X_train)
X_test <- predict(preprocess_params, X_test)
#fitting the model
fit_lasso <- glmnet(X_train, y_train, alpha = 1) # Lasso
fit_ridge <- glmnet(X_train, y_train, alpha = 0) # Ridge
fit_elastic_net <- glmnet(X_train, y_train, alpha = 0.5) # Elastic Net
#cross-validation
cv_lasso <- cv.glmnet(X_train, y_train, alpha = 1) # Lasso CV
cv_ridge <- cv.glmnet(X_train, y_train, alpha = 0) # Ridge CV
cv_elastic_net <- cv.glmnet(X_train, y_train, alpha = 0.5) # Elastic Net CV
#plot coefficient path
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(fit_lasso, xvar = "lambda", label = TRUE,
lwd = 1.5,
main = "Coefficient Path Analysis: LASSO",
cex.main = 0.9,
col = rainbow(10))
abline(v = log(1), col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)
pander(data.frame(colnames(X_train)[c(27, 5, 20, 12)]),caption= "Feature Variables at log(Lambda) = 0")
Feature Variables at log(Lambda) = 0
| Incident.Resolution.Time..in.Hours. |
| Attack.TypeMan-in-the-Middle |
| Attack.SourceUnknown |
| Target.IndustryHealthcare |
pander(data.frame(colnames(X_train)[c(12, 20, 5, 26, 14, 1, 35, 9, 39, 36, 4, 3, 21, 19, 2,6)]),caption= " Feature Variables at log(Lambda) = -1")
Feature Variables at log(Lambda) = -1
| Target.IndustryHealthcare |
| Attack.SourceUnknown |
| Attack.TypeMan-in-the-Middle |
| Defense.Mechanism.UsedVPN |
| Target.IndustryRetail |
| Country |
| NA |
| Target.IndustryBanking |
| NA |
| NA |
| Attack.TypeMalware |
| Attack.TypeDDoS |
| Security.Vulnerability.Type |
| Attack.SourceNation-state |
| Year |
| Attack.TypePhishing |
par(mar=c(5,4,6,3))
##
plot(cv_lasso, main = "RMSE Plot: LASSO",
cex.main = 0.9)
# Extract coefficients for the best lambda
best.lasso.lambda <- cv_lasso$lambda.min
best.ridge.lambda <- cv_ridge$lambda.min
best.elastic.net.lambda <- cv_elastic_net$lambda.min
##
# Lasso Regression (L1 Regularization):
# CAUTION: model formula differs from the regular regression formula
lasso_model.opt <- glmnet(X_train,
y_train,
alpha = 1, # lasso regression
lambda = best.lasso.lambda,standardize = TRUE) # useser selected alpha, optimal lambda
# can be obtained through CV (see below)
lasso_predictions.opt <- predict(lasso_model.opt,
s = best.lasso.lambda, # user selected lambda value
# (regularization paremeter)
newx = X_test) # test data set
# The following RMSE of prediction serves as a validation - one step validation
lasso_rmse.opt <- sqrt(mean((y_test - lasso_predictions.opt)^2))
# Ridge Regression (L2 Regularization)
ridge_model.opt <- glmnet(X_train, y_train, alpha = 0, lambda = best.ridge.lambda,standardize=TRUE)
ridge_predictions.opt <- predict(ridge_model.opt, s = best.ridge.lambda, newx = X_test)
ridge_rmse.opt <- sqrt(mean((y_test - ridge_predictions.opt)^2))
# Elastic Net (Combination of L1 and L2)
elastic_net_model.opt <- glmnet(X_train, y_train, alpha = 0.5, lambda = 0.1,standardize = TRUE)
elastic_net_predictions.opt <- predict(elastic_net_model.opt, s = 0.1, newx = X_test)
elastic_net_rmse.opt <- sqrt(mean((y_test - elastic_net_predictions.opt)^2))
RMSE.opt = cbind(LASSO.opt = lasso_rmse.opt,
Ridge.opt = ridge_rmse.opt,
Elasticnet.opt = elastic_net_rmse.opt)
pander(RMSE.opt)
The optimal lambda for each type of linear regression is shown
above.
LASSO Regression Equation
Now, a final model will be extracted using LASSO, Ridge, and Elastic
Net.
#Lasso
# Extract coefficients with names from glmnet
coef_lasso <- coef(cv_lasso, s = best.lasso.lambda)
coef_df <- as.data.frame(as.matrix(coef_lasso))
coef_df$feature <- rownames(coef_df)
colnames(coef_df)[1] <- "coefficient"
# Filter non-zero coefficients
nonzero_coefs <- subset(coef_df, coefficient != 0)
# Separate intercept
intercept <- round(nonzero_coefs$coefficient[nonzero_coefs$feature == "(Intercept)"], 4)
nonzero_terms <- subset(nonzero_coefs, feature != "(Intercept)")
# Build model equation string
equation <- paste0(round(nonzero_terms$coefficient, 4), "*", nonzero_terms$feature)
cat("Model equation: y =", intercept, "+", paste(equation, collapse = " + "), "\n")
Model equation: y = 50.5369 + 0.6683*Attack.TypeDDoS + -0.4171*Target.IndustryEducation + 0.6626*Target.IndustryGovernment + -0.3464*Target.IndustryHealthcare + 0.265*Attack.SourceHacker Group + -0.5335*Attack.SourceInsider + 0.0065*Defense.Mechanism.UsedAntivirus
Ridge Regression Equation
# Extract coefficients with names from glmnet
coef_ridge <- coef(cv_ridge, s = best.ridge.lambda)
coef_df <- as.data.frame(as.matrix(coef_ridge))
coef_df$feature <- rownames(coef_df)
colnames(coef_df)[1] <- "coefficient"
# Filter non-zero coefficients
nonzero_coefs <- subset(coef_df, coefficient != 0)
# Separate intercept
intercept <- round(nonzero_coefs$coefficient[nonzero_coefs$feature == "(Intercept)"], 4)
nonzero_terms <- subset(nonzero_coefs, feature != "(Intercept)")
# Build model equation string
equation <- paste0(round(nonzero_terms$coefficient, 4), "*", nonzero_terms$feature)
cat("Model equation: y =", intercept, "+", paste(equation, collapse = " + "), "\n")
Model equation: y = 50.5369 + 0.0114*Year + 0.0521*Attack.TypeDDoS + -0.0125*Attack.TypeMalware + 0.0145*Attack.TypeMan-in-the-Middle + -0.0171*Attack.TypePhishing + -0.0201*Attack.TypeRansomware + -0.0186*Attack.TypeSQL Injection + 7e-04*Target.IndustryBanking + -0.0445*Target.IndustryEducation + 0.0576*Target.IndustryGovernment + -0.0398*Target.IndustryHealthcare + 0.0036*Target.IndustryIT + 0.0055*Target.IndustryRetail + 0.0187*Target.IndustryTelecommunications + 6e-04*Number.of.Affected.Users + 0.0427*Attack.SourceHacker Group + -0.0496*Attack.SourceInsider + 0.0182*Attack.SourceNation-state + -0.0106*Attack.SourceUnknown + -0.0045*Defense.Mechanism.UsedAI + 0.0233*Defense.Mechanism.UsedAntivirus + -0.0193*Defense.Mechanism.UsedEncryption + 0.0027*Defense.Mechanism.UsedFirewall + -0.0029*Defense.Mechanism.UsedVPN + -0.0122*Incident.Resolution.Time..in.Hours.
Elastic Net Regression Equation
# Extract coefficients with names from glmnet
coef_elastic <- coef(cv_elastic_net, s = best.elastic.net.lambda)
coef_df <- as.data.frame(as.matrix(coef_elastic))
coef_df$feature <- rownames(coef_df)
colnames(coef_df)[1] <- "coefficient"
# Filter non-zero coefficients
nonzero_coefs <- subset(coef_df, coefficient != 0)
# Separate intercept
intercept <- round(nonzero_coefs$coefficient[nonzero_coefs$feature == "(Intercept)"], 4)
nonzero_terms <- subset(nonzero_coefs, feature != "(Intercept)")
# Build model equation string
equation <- paste0(round(nonzero_terms$coefficient, 4), "*", nonzero_terms$feature)
cat("Model equation: y =", intercept, "+", paste(equation, collapse = " + "), "\n")
Model equation: y = 50.5369 + 0.4941*Attack.TypeDDoS + -0.2438*Target.IndustryEducation + 0.5433*Target.IndustryGovernment + -0.168*Target.IndustryHealthcare + 0.1465*Attack.SourceHacker Group + -0.3984*Attack.SourceInsider
Comparing Linear Regression with SVR
cyber <- read.csv('https://raw.githubusercontent.com/GabbyK8/Datasets/refs/heads/main/Global_Cybersecurity_Threats_2015-2024.csv')
#One-Hot Encoding
dummy <- dummyVars("~.", data=cyber)
cyber2<-data.frame(predict(dummy, newdata=cyber))
#####
# Split data into features (X) and target (y)
X <- cyber2[, -25] # All columns except the target variable
y <- cyber2[, 25] # Target variable (Financial Loss in Millions)
# create dummy variables
#####
# Split data into training and testing sets
set.seed(123)
train.index <- sample(1:nrow(cyber2), 0.8 * nrow(cyber2))
X.train <- X[train.index, ]
y.train <- y[train.index]
X.test <- X[-train.index, ]
y.test <- y[-train.index]
#####
cyber2.train <- as.data.frame(cyber2[train.index, ])
cyber2.test <- cyber2[-train.index, ]
#####
## Set up custom cross-validation control
my_tune_control <- tune.control(
cross = 5, # Use 5-fold cross-validation, the default is 10-fold cross-validation
nrepeat = 1 # Number of repetitions (for repeated cross-validation)
)
#####
# Perform grid search for hyperparameter tuning: RBF kernel is used
tune.RBF <- tune(svm,
train.x = X.train,
train.y = y.train,
ranges = list(epsilon = seq(0.1, 0.5, 0.1),
cost = c(1, 10, 100),
gamma = c(0.01, 0.1, 1)), # Hyperpar in RBF
tunecontrol = my_tune_control # 5-fold cross-validation
)
####
# Display the best parameters
#print(tune.results$best.parameters)
#####
# Train the final model using the best parameters
final.RBF<- svm(X.train, y.train,
type = "eps-regression", # Use "nu-regression" for nu-SVR
kernel = "radial",
epsilon = tune.RBF$best.parameters$epsilon,
cost = tune.RBF$best.parameters$cost,
gamma = tune.RBF$best.parameters$gamma)
#####
# Make predictions on the test set
pred.RBF <- predict(final.RBF, X.test)
# Evaluate performance
mse.RBF <- mean((y.test - pred.RBF)^2) # mean square error
mae.RBF <- mean(abs(y.test - pred.RBF)) # mean absolute error
#### linear kernel
# Perform grid search for hyperparameter tuning: RBF kernel is used
tune.lin <- tune(svm, train.x = X.train, train.y = y.train,
ranges = list(epsilon = seq(0.1, 0.5, 0.1),
cost = c(1, 10, 100)),
tunecontrol = my_tune_control # 5-fold cross-validation
)
####
# Display the best parameters
#print(tune.lin$best.parameters)
#####
# Train the final model using the best parameters
final.lin<- svm(X.train, y.train,
type = "eps-regression", # Use "nu-regression" for nu-SVR
kernel = "linear",
epsilon = tune.lin$best.parameters$epsilon,
cost = tune.lin$best.parameters$cost)
#####
# Make predictions on the test set
pred.lin <- predict(final.lin, X.test)
# Evaluate performance
mse.lin <- mean((y.test - pred.lin)^2) # mean square error
mae.lin <- mean(abs(y.test - pred.lin))
# After tuning is complete
## ordinary LSE regression model with stepwise variable selection
lse.fit <- lm(Financial.Loss..in.Million...~.,data=cyber2.train)
AIC.fit <- stepAIC(lse.fit,direction="both", trace = FALSE)
pred.lse <- predict(AIC.fit, X.test)
mse.lse <- mean((y.test - pred.lse)^2) # mean square error
mae.lse <- mean(abs(y.test - pred.lse)) # mean absolute error
###
par(mfrow=c(2,2), mar=c(2,2,2,2))
plot(AIC.fit)
###
Performance <- data.frame(RBF.SVR=c(mse.RBF, mae.RBF),
Linear.SVR = c(mse.lin, mae.lin),
LSE.Reg =c(mse.lse, mae.lse))
row.names(Performance) <- c("MSE", "MAE")
##
pander(Performance)
| MSE |
811.8 |
829.2 |
810.4 |
| MAE |
24.54 |
24.75 |
24.45 |
Based on our MSE and MAE scores, our least squares estimation has the
best model. It has lower MSE and MAE scores indicating a better fit
model.
LS0tCnRpdGxlOiAiUHJvamVjdCAyIFBhcnQgMSIKYXV0aG9yOiAiR2FicmllbGxhIEtoYWxpbCIKZGF0ZTogIjIwMjUtMDMtMjYiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIGZpZ193aWR0aDogMwogICAgZmlnX2hlaWdodDogMwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHs9aHRtbH0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CgovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8KCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovCiAgZm9udC1zaXplOiAyMnB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7Cn0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLwogIGZvbnQtc2l6ZTogMThweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBuYXZ5OwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9CmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDIycHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLwogICAgZm9udC1zaXplOiAyMHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7CiAgICBmb250LXdlaWdodDogYm9sZDsKfQoKaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9Cgpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQoKLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0KCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9Cgo8L3N0eWxlPgpgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLgppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQogICBsaWJyYXJ5KGtuaXRyKQp9CmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKbGlicmFyeSh0aWR5dmVyc2UpCn0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQpsaWJyYXJ5KEdHYWxseSkKfQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHBhbmRlcikKbGlicmFyeShtb21lbnRzKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGdsbW5ldCkKIyBHZW5lcmFsCmxpYnJhcnkoZTEwNzEpICAgICAgICAjIEZvciBTVk0gICAgICAgIyBGb3IgUmlkZ2UsIExhc3NvLCBsb2dpc3RpYyByZWdyZXNzaW9uCmxpYnJhcnkocFJPQykgICAgICAgICAjIEZvciBST0MgY3VydmVzCmxpYnJhcnkoTWV0cmljcykgICAgICAjIEZvciBNU0UvTUFFCmxpYnJhcnkoTUFTUykKIyBPcHRpb25hbCB2aXN1YWxpemF0aW9uCmxpYnJhcnkoZ3JpZEV4dHJhKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLgogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKCmBgYAoKIyBBbmFseXNpcyBvZiBDeWJlci1zZWN1cml0eSBUaHJlYXRzCgpUaGUgcHVycG9zZSBvZiB0aGlzIGRhdGEgYW5hbHlzaXMgaXMgdG8gaGVscCBtb2RlbCBhbmQgcHJlZGljdCBjeWJlci1zZWN1cml0eSB0aHJlYXRzIHRvIGhlbHAgb3B0aW1pemUgZ2xvYmFsIGRpZ2l0YWwgc2VjdXJpdHkuIEFzIHdlbGwsIHRvIGFuYWx5emUgd2hpY2ggZGVmZW5zZSBtZWNoYW5pc21zIHRoYXQgYXJlIG9wdGltYWwgaW4gY29tYmF0aW5nIGN5YmVyIGF0dGFja3MuIFRoZSBkYXRhc2V0IGNvbnRhaW5zIDEwIHZhcmlhYmxlcyB0aGF0IGFyZSBwb3RlbnRpYWwgc2lnbmlmaWNhbnQgY292YXJpYXRlcyBpbiBwcmVkaWN0aW5nIHBvdGVudGlhbCB0aHJlYXRzLiBUaGUgdmFyaWFibGUgY291bnRyeSByZWZlcnMgdG8gdGhlIGNvdW50cnkgd2hlcmUgdGhlIGF0dGFjayBvY2N1cnJlZC4gVGhlIG90aGVyIHZhcmlhYmxlcyBhbmFseXplIGluY2lkZW50IHJlc29sdXRpb24gdGltZSBpbiBob3VycywgbnVtYmVyIG9mIGFmZmVjdGVkIHVzZXJzLCBmaW5hbmNpYWwgbG9zcywgYXR0YWNrIHR5cGUsIHRhcmdldCBpbmR1c3RyeSwgYXR0YWNrIHNvdXJjZSwgc2VjdXJpdHkgdnVsbmVyYWJpbGl0eSB0eXBlIGFuZCBkZWZlbnNlIG1lY2hhbmlzbSB1c2VkLiBIZXJlIGlzIGEgZnVsbCBsaXN0IG9mIHZhcmlhYmxlczoKCi0gICAqKkNvdW50cnkqKiBjb3VudHJ5IHdoZXJlIHRoZSBhdHRhY2sgb2NjdXJyZWQKLSAgICoqWWVhcioqIHllYXIgb2YgdGhlIGluY2lkZW50Ci0gICAqKkF0dGFjayBUeXBlKiogdHlwZSBvZiBjeWJlci1zZWN1cml0eSB0aHJlYXQKLSAgICoqVGFyZ2V0IEluZHVzdHJ5KiogaW5kdXN0cnkgdGFyZ2V0ZWQKLSAgICoqRmluYW5jaWFsIExvc3MqKiBlc3RpbWF0ZWQgZmluYW5jaWFsIGxvc3MgaW4gbWlsbGlvbnMKLSAgICoqTnVtYmVyIG9mIEFmZmVjdGVkIFVzZXJzKiogbnVtYmVyIG9mIHVzZXJzIGltcGFjdGVkIGJ5IHRoZSBhdHRhY2sKLSAgICoqQXR0YWNrIFNvdXJjZSoqIG9yaWdpbiBvZiB0aGUgYXR0YWNrCi0gICAqKlNlY3VyaXR5IFZ1bG5lcmFiaWxpdHkgVHlwZSoqIHR5cGUgb2YgdnVsbmVyYWJpbGl0eSBleHBsb2l0ZWQKLSAgICoqRGVmZW5zZSBNZWNoYW5pc20gVXNlZCoqIGN5YmVyLXNlY3VyaXR5IGRlZmVuc2Ugc3RyYXRlZ3kgYXBwbGllZAotICAgKipJbmNpZGVudCBSZXNvbHV0aW9uIFRpbWUqKiB0aW1lIHRha2VuIHRvIGZ1bGx5IHJlc29sdmUgZXZlbnQgaW4gaG91cnMKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhpcyBwbG90IHNob3dzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzZWN1cml0eSB2dWxuZXJhYmlsaXR5IHR5cGUgYW5kIHRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgYWZmZWN0ZWQgdXNlcnMgYW5kIGZpbmFuY2lhbCBsb3NzIGluIG1pbGxpb25zLiBJbiBtb3N0IGNhc2VzIHRoZSBudW1iZXIgb2YgYWZmZWN0ZWQgdXNlcnMgc2VlbXMgdG8gYmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggZmluYW5jaWFsIGxvc3MuIFRoZSBvbmx5IGV4Y2VwdGlvbiBpcyBzZWVuIGluIHplcm8tZGF5LiB6ZXJvLWRheSByZWZlcnMgdG8gYSB0eXBlIG9mIHZ1bG5lcmFiaWxpdHkgd2hlcmUgdGhlIHRhcmdldCBvZiB0aGUgYXR0YWNrIGlzIHVuYXdhcmUgb2YgdGhlIGF0dGFjay4gTWVhbmluZyB0aGVyZSBhcmUgemVybyBkYXlzIGZvciB0aGUgcHJvYmxlbSB0byBiZSBzb2x2ZWQgYmVmb3JlIGl0IGhhcyBiZWVuIGV4cGxvaXRlZC4gSXQgd291bGQgbWFrZSBzZW5zZSB0aGF0IGluIHRoaXMgdHlwZSBvZiB2dWxuZXJhYmlsaXR5LCB0aGUgZmV3ZXIgdXNlcnMgYWZmZWN0ZWQgd291bGQgbGVhZCB0byBhIGdyZWF0ZXIgZmluYW5jaWFsIGxvc3MuJ30KY3liZXIgPC0gcmVhZC5jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9HYWJieUs4L0RhdGFzZXRzL3JlZnMvaGVhZHMvbWFpbi9HbG9iYWxfQ3liZXJzZWN1cml0eV9UaHJlYXRzXzIwMTUtMjAyNC5jc3YnKQpjeWJlciREZWZlbnNlLk1lY2hhbmlzbS5Vc2VkIDwtIGlmZWxzZShjeWJlciREZWZlbnNlLk1lY2hhbmlzbS5Vc2VkID09ICJBSS1iYXNlZCBEZXRlY3Rpb24iLCAiQUkiLCBjeWJlciREZWZlbnNlLk1lY2hhbmlzbS5Vc2VkKQoKIyBwbG90IDEgbnVtYmVyIG9mIGFmZmVjdGVkIHVzZXJzIGJ5IGZpbmFuY2lhbCBsb3NzIGJ5IHZ1bG5lcmFiaWxpdHkgdHlwZQoKCmdncGxvdChjeWJlciwgYWVzKHggPSBjeWJlciROdW1iZXIub2YuQWZmZWN0ZWQuVXNlcnMsIHkgPSBjeWJlciRGaW5hbmNpYWwuTG9zcy4uaW4uTWlsbGlvbi4uLiwgY29sb3IgPSBjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGUpKSArCmdlb21fc21vb3RoKGFlcyh4ID0gY3liZXIkTnVtYmVyLm9mLkFmZmVjdGVkLlVzZXJzLCB5ID0gY3liZXIkRmluYW5jaWFsLkxvc3MuLmluLk1pbGxpb24uLi4sIGNvbG9yID0gY3liZXIkU2VjdXJpdHkuVnVsbmVyYWJpbGl0eS5UeXBlKSwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDIsIDEwKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJQbG90IG9mIFNlY3VyaXR5IFZ1bG5lcmFiaWxpdHksIFZvbHVtZSBDb21wcm9taXNlZCwgYW5kIEZpbmFuY2lhbCBJbXBhY3QiLAogICAgeCA9ICJWb2x1bWUgQ29tcHJvbWlzZWQiLAogICAgeSA9ICJGaW5hbmNpYWwgSW1wYWN0IiwKICAgIGNvbG9yID0gIlNlY3VyaXR5IFZ1bGVybmFiaWxpdHkiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGlzIHBsb3Qgc2hvd3MgdGhlIGRlbnNpdHkgb2YgbnVtYmVyIG9mIGFmZmVjdGVkIHVzZXJzIGluIGN5YmVyc2VjdXJpdHkgYXR0YWNrcyBpbiBlYWNoIGNvdW50cnkgaW4gdGhlIHllYXIgMjAxNS4gJ30KIyBwbG90IDMgRGVuc2l0eSBwbG90IG9mIGF0dGFja2VkIGluZHVzdHJpZXMKWWVhcl9kYXRhIDwtIGN5YmVyICU+JQogIGZpbHRlcihZZWFyID09IDIwMTUpCnAxPC0gZ2dwbG90KFllYXJfZGF0YSwgYWVzKHggPSBOdW1iZXIub2YuQWZmZWN0ZWQuVXNlcnMsIGZpbGwgPSBDb3VudHJ5KSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNikgKwogIGxhYnMoCiAgICB4ID0gIk51bWJlciBvZiBBZmZlY3RlZCBVc2VycyIsCiAgICB5ID0gIkRlbnNpdHkiLAogICAgZmlsbD0iQ291bnRyeSIsCiAgICB0aXRsZSA9ICJEZW5zaXR5IFBsb3Qgb2YgQXR0YWNrZWQgQ291bnRyaWVzIGluIDIwMTUiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmdncGxvdGx5KHAxKQpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0iVGhpcyBwbG90IHNob3dzIHRoZSBkZW5zaXR5IG9mIG51bWJlciBvZiBhZmZlY3RlZCB1c2VycyBpbiBjeWJlcnNlY3VyaXR5IGF0dGFja3MgaW4gZWFjaCBjb3VudHJ5IGluIHRoZSB5ZWFyIDIwMjQuICJ9CiBZZWFyX2RhdGExIDwtIGN5YmVyICU+JQogIGZpbHRlcihZZWFyID09IDIwMjQpCiMgcGxvdCA0IERlbnNpdHkgcGxvdCBvZiBBdHRhY2tlZCBjb3VudHJpZXMKcDIgPC0gZ2dwbG90KFllYXJfZGF0YTEsIGFlcyh4ID0gTnVtYmVyLm9mLkFmZmVjdGVkLlVzZXJzLCBmaWxsID0gQ291bnRyeSkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjYpICsKICBsYWJzKAogICAgeCA9ICJOdW1iZXIgb2YgQWZmZWN0ZWQgVXNlcnMiLAogICAgeSA9ICJEZW5zaXR5IiwKICAgIGZpbGw9IkNvdW50cnkiLAogICAgdGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIEF0dGFja2VkIENvdW50cmllcyBpbiAyMDI0IgogICkgKwogIHRoZW1lX21pbmltYWwoKQpnZ3Bsb3RseShwMikKCmBgYAoKRWFjaCBjb3VudHJ5IGlzIGdpdmVuIGEgc3BlY2lmaWMgY29sb3IgYW5kIHRoZSBudW1iZXIgb2YgYWZmZWN0ZWQgdXNlcnMgaXMgZGlzcGxheWVkIG9uIHRoZSB4LWF4aXMuIFRoZSBkZW5zaXR5IGlzIGRpc3BsYXllZCBvbiB0aGUgeS1heGlzLiBEZW5zaXR5IGluIHRoaXMgcGxvdCBzaG93cyB0aGUgcmVsYXRpdmUgZnJlcXVlbmN5IG9mIG51bWJlciBvZiBhZmZlY3RlZCB1c2VycyBwZXIgYXR0YWNrIGluIGVhY2ggY291bnRyeS5JbiAyMDE1LCB0aGUgY3liZXIgYXR0YWNrcyB3aXRoaW4gdGhlIFVTQSBtb3JlIGZyZXF1ZW50bHkgYWZmZWN0ZWQgYWJvdXQgMzEwLDAwMCB1c2VycywgY29tcGFyZWQgdG8gUnVzc2lhIHdoZXJlIGF0dGFja3MgbW9yZSBmcmVxdWVudGx5IGF0dGFja2VkIDY3MCwwMDAgdXNlcnMgYW5kIEdlcm1hbnkgd2hlcmUgbW9zdCBhdHRhY2tzIG1vcmUgZnJlcXVlbnRseSBhZmZlY3RlZCA3OTAsMDAwIHVzZXJzLiBJbiAyMDI0LCB0aGUgbnVtYmVyIG9mIGFmZmVjdGVkIHVzZXJzIGluIHRoZSBVU0EgZG9lcyBub3Qgc2VlbSB0byBiZSBtdWNoIG1vcmUgZGVuc2UgaW4gYW55IG9uZSBudW1iZXIgYW5kIGlzIG1vcmUgZGlzcGVyc2VkLiBKYXBhbiBoYWQgbW9yZSBmcmVxdWVudCBhdHRhY2tzIHRoYXQgYWZmZWN0ZWQgNTAwLDAwMCBhbmQgQnJhemlsIGhhZCBpdCdzIG1vc3QgZnJlcXVlbnQgYXQgNzUwLDAwMCB1c2Vycy4KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhpcyBwbG90IHNob3dzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzZWN1cml0eSB2dWxuZXJhYmlsaXR5IHR5cGUgYW5kIHRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRoZSB5ZWFyIG9mIGF0dGFjayBhbmQgcmVzb2x1dGlvbiB0aW1lLiBUaGUgcGxvdCBzaG93cyBob3cgb3ZlciB0aGUgeWVhcnMgcmVzb2x1dGlvbiB0aW1lIGhhcyBpbXByb3ZlZCBmb3IgY2VydGFpbiB2dWxuZXJhYmlsaXR5IHR5cGVzLCBzdWNoIGFzIHdlYWsgcGFzc3dvcmRzLCB1bnBhdGNoZWQgc29mdHdhcmUsIGFuZCBzb2NpYWwgZW5naW5lZXJpbmcuIEhvd2V2ZXIsIGZvciB6ZXJvLWRheSB0aGVyZSBpcyBhbiBpbmNyZWFzZSBpbiByZXNvbHV0aW9uIHRpbWUsIHNob3dpbmcgYSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB6ZXJvLWRheSBhbmQgcmVzb2x1dGlvbiB0aW1lLiAnfQoKZ2dwbG90KGN5YmVyLCBhZXMoeCA9IGN5YmVyJFllYXIsIHkgPSBjeWJlciRJbmNpZGVudC5SZXNvbHV0aW9uLlRpbWUuLmluLkhvdXJzLiwgY29sb3IgPSBjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGUpKSArCmdlb21fc21vb3RoKGFlcyh4ID0gY3liZXIkWWVhciwgeSA9IGN5YmVyJEluY2lkZW50LlJlc29sdXRpb24uVGltZS4uaW4uSG91cnMuLCBjb2xvciA9IGN5YmVyJFNlY3VyaXR5LlZ1bG5lcmFiaWxpdHkuVHlwZSksIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygyLCAxMCkpICsKICBsYWJzKAogICAgdGl0bGUgPSAiUGxvdCBvZiBTZWN1cml0eSBWdWxuZXJhYmlsaXR5LCBZZWFyIG9mIEF0dGFjaywgYW5kIFJlc29sdXRpb24gVGltZSIsCiAgICB4ID0gIlllYXIgb2YgQXR0YWNrIiwKICAgIHkgPSAiSW5jaWRlbnQgUmVzb2x1dGlvbiBUaW1lIGluIEhvdXJzIiwKICAgIGNvbG9yID0gIlNlY3VyaXR5IFZ1bGVybmFiaWxpdHkiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCgpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhpcyBwbG90IGZvY3VzZXMgb24gemVyby1kYXkgdnVsbmVyYWJpbGl0eSBhbmQgc2hvd3MgdGhlIHByb3BvcnRpb24gb2YgdGhyZWF0IHR5cGVzIGluIGVhY2ggaW5kdXN0cnkuIFRoZSB4LWF4aXMgc2hvd3MgZWFjaCB0aHJlYXQgdHlwZSBhbmQgdGhlIHktYXhpcyBzaG93cyB0aGUgcHJvcG9ydGlvbiBvZiBhdHRhY2tzIHdpdGhpbiB0aGUgZGF0YXNldCB0aGF0IGZhbGwgaW50byBlYWNoIGNhdGVnb3J5LiBUaGUgZGlmZmVyZW50IGNvbG9ycyBhcmUgbWVhbnQgdG8gc2hvdyBhIHN1YmNhdGVnb3J5LCB0YXJnZXQgaW5kdXN0cnkuIFRoaXMgc2hvd3MgdGhlIHByb3BvcnRpb24gb2YgdGhlIGRhdGFzZXQgdGhhdCBmYWxscyB3aXRoaW4gZWFjaCBjYXRlZ29yeSBvZiB0aHJlYXQgdHlwZSBpbiBlYWNoIHRhcmdldCBpbmR1c3RyeS4nfQojcGxvdCBvZiB6ZXJvLWRheSBhdHRhY2sgdHlwZSBieSB0YXJnZXQgaW5kdXN0cnkKemVyb19kYXRhIDwtIGN5YmVyICU+JQogIGZpbHRlcihTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGUgPT0gIlplcm8tZGF5IikgCmEgPC0gZ2dwbG90KHplcm9fZGF0YSwgYWVzKHggPSB6ZXJvX2RhdGEkQXR0YWNrLlR5cGUsIGZpbGwgPXplcm9fZGF0YSRUYXJnZXQuSW5kdXN0cnkgKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikgKwogIGxhYnMoCiAgICB4ID0gIlRocmVhdCBUeXBlIiwKICAgIHkgPSAiUHJvcG9ydGlvbiIsCiAgICBmaWxsID0gIlRhcmdldCBJbmR1c3RyeSIsCiAgICB0aXRsZSA9ICJaZXJvLWRheSBJbmNpZGVuY2VzIGJ5IFRocmVhdCBUeXBlIHZzLiBUYXJnZXQgSW5kdXN0cnkiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmdncGxvdGx5KGEpCmBgYAoKWmVyby1kYXkgYXR0YWNrcyBzZWVtIHRvIGNvbW1vbmx5IHVzZSBQaGlzaW5nIHdpdGhpbiBib3RoIHJldGFpbCBhbmQgSVQgaW5kdXN0cmllcywgYmFzZWQgb24gdGhlIGhlaWdodCBhbmQgY291bnQgb2YgdGhlIGJhciBpbiB0aGVzZSB0d28gY2F0ZWdvcmllcy4gUmFuc29td2FyZSBpcyB0aGUgbW9zdCBtaW5pbWFsbHkgdXNlZCB6ZXJvLWRheSBhdHRhY2sgb24gdGhlIGdvdmVybm1lbnQsIHdpdGggYSBjb3VudCBvZiBqdXN0IDEyLgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgYmxveHBsb3Qgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRlZmVuc2UgbWVjaGFuaXNtIGFuZCByZXNvbHV0aW9uIHRpbWUgaW4gaG91cnMuJ30KCmNvbDA9YygiIzQ2ODJCNCIsICIjQjQ0NjRCIiwiIzk5MDBGRiIsIiNGRjY2RkYiLCAiIzAwOTkzMyIpCmJveHBsb3QoSW5jaWRlbnQuUmVzb2x1dGlvbi5UaW1lLi5pbi5Ib3Vycy4gfiBEZWZlbnNlLk1lY2hhbmlzbS5Vc2VkLAogICAgICAgIGRhdGE9Y3liZXIsCiAgICAgICAgeGxhYj0iRGVmZW5zZSBNZWNoYW5pc20iLAogICAgICAgIHlsYWI9IlJlc29sdXRpb24gVGltZSAoaG91cnMpIiwKICAgICAgICBjb2wgPSBjb2wwLAogICAgICAgIG1haW49IkluY2lkZW50IFJlc29sdXRpb24gVGltZSBieSBEZWZlbnNlIE1lY2hhbmlzbSIsCiAgICAgICAgY2V4Lm1haW4gPSAwLjksCiAgICAgICAgY29sLm1haW4gPSAiYmx1ZSIpCgoKYGBgCgpUaGUgYm94cGxvdCBzaG93cyB0aGF0IHRoZSBhdmVyYWdlIHJlc29sdXRpb24gdGltZSBhbW9uZyBhdHRhY2tzIGJhc2VkIG9uIGRlZmVuc2UgbWVjaGFuaXNtcyBpcyByZWxhdGl2ZWx5IHRoZSBzYW1lLiBUaGUgb25seSBleGNlcHRpb24gc2VlbXMgdG8gYmUgZmlyZXdhbGwsIHdoaWNoIGFwcGVhcnMgdG8gdGFrZSBzbGlnaHRseSBsZXNzIHJlc29sdXRpb24gdGltZSBvbiBhdmVyYWdlLldlIGNhbiBpbmRpY2F0ZSB0aGUgYXZlcmFnZSBieSBsb29raW5nIHRoZSBibGFjayBob3Jpem9udGFsIGxpbmUgd2l0aGluIGVhY2ggYm94LiBUaGV5IGFsbCBhcHBlYXIgdG8gbGluZSB1cCB3aXRoIG9uZSBhbm90aGVyIGluZGljYXRpbmcgc2ltaWxhciBhdmVyYWdlcy4KCiMgU2tld25lc3MKCmBgYHtyfQoKY3liZXIkQ291bnRyeVtjeWJlciRDb3VudHJ5PT0iVVNBIl08LTEKY3liZXIkQ291bnRyeTwtIGlmZWxzZShjeWJlciRDb3VudHJ5IT0iVVNBIiwwKQpjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGVbY3liZXIkU2VjdXJpdHkuVnVsbmVyYWJpbGl0eS5UeXBlPT0iWmVyby1kYXkiXTwtMQpjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGU8LWlmZWxzZShjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGUhPSJaZXJvLWRheSIsMCkKY3liZXIkTnVtYmVyLm9mLkFmZmVjdGVkLlVzZXJzPC1hcy5udW1lcmljKGN5YmVyJE51bWJlci5vZi5BZmZlY3RlZC5Vc2VycykKY3liZXIkWWVhcjwtYXMubnVtZXJpYyhhcy5mYWN0b3IoY3liZXIkWWVhcikpCmN5YmVyJEluY2lkZW50LlJlc29sdXRpb24uVGltZS4uaW4uSG91cnMuPC1hcy5udW1lcmljKGN5YmVyJEluY2lkZW50LlJlc29sdXRpb24uVGltZS4uaW4uSG91cnMuKQoKCmM8LXNrZXduZXNzKGN5YmVyJEZpbmFuY2lhbC5Mb3NzLi5pbi5NaWxsaW9uLi4uKQpkPC1za2V3bmVzcyhjeWJlciROdW1iZXIub2YuQWZmZWN0ZWQuVXNlcnMpCmU8LXNrZXduZXNzKGN5YmVyJFllYXIpIAoKU2tld25lc3MgPSBjYmluZChGaW5hbmNpYWwuTG9zcyA9IGMsIAogICAgICAgICAgICAgICAgIEFmZmVjdGVkLlVzZXJzID0gIGQsIAogICAgICAgICAgICAgICAgIFllYXIgPSBlKQoKcGFuZGVyKFNrZXduZXNzKQpgYGAKClRoZSBkYXRhIGlzIG5vcm1hbGx5IHNrZXdlZCBhbmQgbm8gdHJhbnNmb3JtYXRpb24gaXMgbmVjZXNzYXJ5LgoKIyBMaW5lYXIgUmVncmVzc2lvbgoKTGluZWFyIHJlZ3Jlc3Npb24gaXMgZXhwbG9yZWQgYmVsb3c6CgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMiwgZmlnLmNhcD0nVGhlIHgtYXhpcyBvZiB0aGUgY29lZmZpY2llbnQgcGF0aCBhbmFseXNpcyBzaG93cyB0aGUgbG9nIG9mIGxhbWJkYS4gQSBsb3cgbGFtYmRhIGhhcyBsZXNzIHJlZ3VsYXJpemF0aW9uIGFuZCBpcyBjbG9zZXIgdG8gb3JkaW5hcnkgbGVhc3Qgc3F1YXJlcy4gQSBoaWdoIGxhbWJkYSBpbmRpY2F0ZXMgaGlnaGVyIHJlZ3VsYXJpemF0aW9uIGFuZCBhdCB0aGlzIGxhbWJkYSBtYW55IGNvZWZmaWNpZW50cyB3aWxsIHNocmluayB0byB6ZXJvLiBUaGUgWS1heGlzIHNob3dzIHRoZSBjb2VmZmljaWVudCB2YWx1ZXMgZm9yIGVhY2ggdmFyaWFibGUgcHJlZGljdG9yIGZvciBmaW5hbmNpYWwgbG9zcy4gVGhlIGxpbmVzIHNob3dzIHRoZSBwYXRoIG9mIGVhY2ggY29lZmZpY2llbnQgaGFzIGxhbWJkYSBkaWZmZXJzLiBUaGUgZGFzaGVkIGxpbmUgc2hvd3MgdGhlIG9wdGltYWwgbGV2ZWwgb2Ygc2hyaW5rYWdlLiBDb2VmZmljaWVudHMgdG8gdGhlIHJpZ2h0IG9mIHRoaXMgbGluZSBhcmUgbW9yZSBmb3JjZWQgdG93YXJkcyB6ZXJvLiBBdCB0aGlzIGNob3NlbiBsYW1iZGEgb25seSBjZXJ0YWluIHZhcmlhYmxlcyB3aWxsIGJlIG5vbi16ZXJvIGFuZCBhcmUgZGVlbWVkIHNpZ25pZmljYW50LiAnfQoKCgoKIyBQcmVkaWN0b3JzICh4KSBhbmQgUmVzcG9uc2UgVmFyaWFibGUgKHkpCgpzZXQuc2VlZCgxMjM0NSkKWCA8LSBhcy5kYXRhLmZyYW1lKGN5YmVyWywgLTVdKSAgCgp5IDwtIGN5YmVyJEZpbmFuY2lhbC5Mb3NzLi5pbi5NaWxsaW9uLi4uCgoKdHJhaW5faW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5LCBwID0gMC44LCBsaXN0ID0gRkFMU0UpClhfdHJhaW4gPC0gbWFrZVgoWFt0cmFpbl9pbmRleCwgXSkKWF90ZXN0IDwtIG1ha2VYKFhbLXRyYWluX2luZGV4LCBdKQp5X3RyYWluIDwtIHlbdHJhaW5faW5kZXhdCnlfdGVzdCA8LSB5Wy10cmFpbl9pbmRleF0KCiNzdGFuZGFyZGl6aW5nIHRoZSBkYXRhCnByZXByb2Nlc3NfcGFyYW1zIDwtIHByZVByb2Nlc3MoWF90cmFpbiwgbWV0aG9kID0gYygiY2VudGVyIiwic2NhbGUiKSkKWF90cmFpbiA8LSBwcmVkaWN0KHByZXByb2Nlc3NfcGFyYW1zLCBYX3RyYWluKQpYX3Rlc3QgPC0gcHJlZGljdChwcmVwcm9jZXNzX3BhcmFtcywgWF90ZXN0KQoKCiNmaXR0aW5nIHRoZSBtb2RlbApmaXRfbGFzc28gPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMSkgICMgTGFzc28KZml0X3JpZGdlIDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDApICAjIFJpZGdlCmZpdF9lbGFzdGljX25ldCA8LSBnbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwLjUpICAjIEVsYXN0aWMgTmV0CiNjcm9zcy12YWxpZGF0aW9uCmN2X2xhc3NvIDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDEpICAgICAgICMgTGFzc28gQ1YKY3ZfcmlkZ2UgPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCkgICAgICAgIyBSaWRnZSBDVgpjdl9lbGFzdGljX25ldCA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwLjUpICAjIEVsYXN0aWMgTmV0IENWCgojcGxvdCBjb2VmZmljaWVudCBwYXRoCnBhcihtYXI9Yyg1LDQsNiwzKSkKIyBQbG90IGNvZWZmaWNpZW50IHBhdGgKcGxvdChmaXRfbGFzc28sIHh2YXIgPSAibGFtYmRhIiwgbGFiZWwgPSBUUlVFLAogICAgIGx3ZCA9IDEuNSwKICAgICBtYWluID0gIkNvZWZmaWNpZW50IFBhdGggQW5hbHlzaXM6IExBU1NPIiwKICAgICBjZXgubWFpbiA9IDAuOSwKICAgICBjb2wgPSByYWluYm93KDEwKSkKYWJsaW5lKHYgPSBsb2coMSksIGNvbCA9ICJwdXJwbGUiLCBsdHkgPSA0LCBsd2QgPSAyKQphYmxpbmUodiA9IC0xLCBjb2wgPSAic3RlZWxibHVlIiwgbHR5ID0gMiwgbHdkID0gMikKCmBgYAoKYGBge3J9CgpwYW5kZXIoZGF0YS5mcmFtZShjb2xuYW1lcyhYX3RyYWluKVtjKDI3LCA1LCAyMCwgMTIpXSksY2FwdGlvbj0gIkZlYXR1cmUgVmFyaWFibGVzIGF0IGxvZyhMYW1iZGEpID0gMCIpCgpwYW5kZXIoZGF0YS5mcmFtZShjb2xuYW1lcyhYX3RyYWluKVtjKDEyLCAyMCwgNSwgMjYsIDE0LCAxLCAzNSwgOSwgMzksIDM2LCA0LCAzLCAyMSwgMTksIDIsNildKSxjYXB0aW9uPSAiIEZlYXR1cmUgVmFyaWFibGVzIGF0IGxvZyhMYW1iZGEpID0gLTEiKQpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhlIHBsb3Qgb2YgcGVyZm9ybWFuY2Ugc2hvd3MgdGhhdCBhcyBsYW1iZGEgaW5jcmVhc2VzLCB0aGUgTVNFIGRlY3JlYXNlcy4gVGhlIHR3byB2ZXJ0aWNhbCBsaW5lcyBnaXZlIGEgcmVmZXJlbmNlIG9mIGxhbWJkYS4gJ30KcGFyKG1hcj1jKDUsNCw2LDMpKQojIwpwbG90KGN2X2xhc3NvLCBtYWluID0gIlJNU0UgUGxvdDogTEFTU08iLAogICAgIGNleC5tYWluID0gMC45KQpgYGAKCmBgYHtyfQojIEV4dHJhY3QgY29lZmZpY2llbnRzIGZvciB0aGUgYmVzdCBsYW1iZGEKYmVzdC5sYXNzby5sYW1iZGEgPC0gY3ZfbGFzc28kbGFtYmRhLm1pbgpiZXN0LnJpZGdlLmxhbWJkYSA8LSBjdl9yaWRnZSRsYW1iZGEubWluCmJlc3QuZWxhc3RpYy5uZXQubGFtYmRhIDwtIGN2X2VsYXN0aWNfbmV0JGxhbWJkYS5taW4KIyMKIyBMYXNzbyBSZWdyZXNzaW9uIChMMSBSZWd1bGFyaXphdGlvbik6IAojIENBVVRJT046IG1vZGVsIGZvcm11bGEgZGlmZmVycyBmcm9tIHRoZSByZWd1bGFyIHJlZ3Jlc3Npb24gZm9ybXVsYSAKbGFzc29fbW9kZWwub3B0IDwtIGdsbW5ldChYX3RyYWluLCAKICAgICAgICAgICAgICAgICAgICAgIHlfdHJhaW4sIAogICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLCAgICAgICMgbGFzc28gcmVncmVzc2lvbiAKICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYSA9IGJlc3QubGFzc28ubGFtYmRhLHN0YW5kYXJkaXplID0gVFJVRSkgICAjIHVzZXNlciBzZWxlY3RlZCBhbHBoYSwgb3B0aW1hbCBsYW1iZGEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjYW4gYmUgb2J0YWluZWQgdGhyb3VnaCBDViAoc2VlIGJlbG93KQpsYXNzb19wcmVkaWN0aW9ucy5vcHQgPC0gcHJlZGljdChsYXNzb19tb2RlbC5vcHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMgPSBiZXN0Lmxhc3NvLmxhbWJkYSwgIyB1c2VyIHNlbGVjdGVkIGxhbWJkYSB2YWx1ZSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIChyZWd1bGFyaXphdGlvbiBwYXJlbWV0ZXIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3eCA9IFhfdGVzdCkgICMgdGVzdCBkYXRhIHNldCAKIyBUaGUgZm9sbG93aW5nIFJNU0Ugb2YgcHJlZGljdGlvbiBzZXJ2ZXMgYXMgYSB2YWxpZGF0aW9uIC0gb25lIHN0ZXAgdmFsaWRhdGlvbgpsYXNzb19ybXNlLm9wdCA8LSBzcXJ0KG1lYW4oKHlfdGVzdCAtIGxhc3NvX3ByZWRpY3Rpb25zLm9wdCleMikpICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojIFJpZGdlIFJlZ3Jlc3Npb24gKEwyIFJlZ3VsYXJpemF0aW9uKQpyaWRnZV9tb2RlbC5vcHQgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCwgbGFtYmRhID0gYmVzdC5yaWRnZS5sYW1iZGEsc3RhbmRhcmRpemU9VFJVRSkKcmlkZ2VfcHJlZGljdGlvbnMub3B0IDwtIHByZWRpY3QocmlkZ2VfbW9kZWwub3B0LCBzID0gYmVzdC5yaWRnZS5sYW1iZGEsIG5ld3ggPSBYX3Rlc3QpCnJpZGdlX3Jtc2Uub3B0IDwtIHNxcnQobWVhbigoeV90ZXN0IC0gcmlkZ2VfcHJlZGljdGlvbnMub3B0KV4yKSkKCiMgRWxhc3RpYyBOZXQgKENvbWJpbmF0aW9uIG9mIEwxIGFuZCBMMikKZWxhc3RpY19uZXRfbW9kZWwub3B0IDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAuNSwgbGFtYmRhID0gMC4xLHN0YW5kYXJkaXplID0gVFJVRSkKZWxhc3RpY19uZXRfcHJlZGljdGlvbnMub3B0IDwtIHByZWRpY3QoZWxhc3RpY19uZXRfbW9kZWwub3B0LCBzID0gMC4xLCBuZXd4ID0gWF90ZXN0KQplbGFzdGljX25ldF9ybXNlLm9wdCA8LSBzcXJ0KG1lYW4oKHlfdGVzdCAtIGVsYXN0aWNfbmV0X3ByZWRpY3Rpb25zLm9wdCleMikpCgpSTVNFLm9wdCA9IGNiaW5kKExBU1NPLm9wdCA9IGxhc3NvX3Jtc2Uub3B0LCAKICAgICAgICAgICAgICAgICBSaWRnZS5vcHQgPSAgcmlkZ2Vfcm1zZS5vcHQsIAogICAgICAgICAgICAgICAgIEVsYXN0aWNuZXQub3B0ID0gZWxhc3RpY19uZXRfcm1zZS5vcHQpCgpwYW5kZXIoUk1TRS5vcHQpCmBgYAoKVGhlIG9wdGltYWwgbGFtYmRhIGZvciBlYWNoIHR5cGUgb2YgbGluZWFyIHJlZ3Jlc3Npb24gaXMgc2hvd24gYWJvdmUuCgojIyBMQVNTTyBSZWdyZXNzaW9uIEVxdWF0aW9uCgpOb3csIGEgZmluYWwgbW9kZWwgd2lsbCBiZSBleHRyYWN0ZWQgdXNpbmcgTEFTU08sIFJpZGdlLCBhbmQgRWxhc3RpYyBOZXQuCgpgYGB7cn0KI0xhc3NvCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgd2l0aCBuYW1lcyBmcm9tIGdsbW5ldApjb2VmX2xhc3NvIDwtIGNvZWYoY3ZfbGFzc28sIHMgPSBiZXN0Lmxhc3NvLmxhbWJkYSkKY29lZl9kZiA8LSBhcy5kYXRhLmZyYW1lKGFzLm1hdHJpeChjb2VmX2xhc3NvKSkKY29lZl9kZiRmZWF0dXJlIDwtIHJvd25hbWVzKGNvZWZfZGYpCmNvbG5hbWVzKGNvZWZfZGYpWzFdIDwtICJjb2VmZmljaWVudCIKCiMgRmlsdGVyIG5vbi16ZXJvIGNvZWZmaWNpZW50cwpub256ZXJvX2NvZWZzIDwtIHN1YnNldChjb2VmX2RmLCBjb2VmZmljaWVudCAhPSAwKQoKIyBTZXBhcmF0ZSBpbnRlcmNlcHQKaW50ZXJjZXB0IDwtIHJvdW5kKG5vbnplcm9fY29lZnMkY29lZmZpY2llbnRbbm9uemVyb19jb2VmcyRmZWF0dXJlID09ICIoSW50ZXJjZXB0KSJdLCA0KQpub256ZXJvX3Rlcm1zIDwtIHN1YnNldChub256ZXJvX2NvZWZzLCBmZWF0dXJlICE9ICIoSW50ZXJjZXB0KSIpCgojIEJ1aWxkIG1vZGVsIGVxdWF0aW9uIHN0cmluZwplcXVhdGlvbiA8LSBwYXN0ZTAocm91bmQobm9uemVyb190ZXJtcyRjb2VmZmljaWVudCwgNCksICIqIiwgbm9uemVyb190ZXJtcyRmZWF0dXJlKQpjYXQoIk1vZGVsIGVxdWF0aW9uOiB5ID0iLCBpbnRlcmNlcHQsICIrIiwgcGFzdGUoZXF1YXRpb24sIGNvbGxhcHNlID0gIiArICIpLCAiXG4iKQoKCmBgYAoKIyMgUmlkZ2UgUmVncmVzc2lvbiBFcXVhdGlvbgoKYGBge3J9CgojIEV4dHJhY3QgY29lZmZpY2llbnRzIHdpdGggbmFtZXMgZnJvbSBnbG1uZXQKY29lZl9yaWRnZSA8LSBjb2VmKGN2X3JpZGdlLCBzID0gYmVzdC5yaWRnZS5sYW1iZGEpCmNvZWZfZGYgPC0gYXMuZGF0YS5mcmFtZShhcy5tYXRyaXgoY29lZl9yaWRnZSkpCmNvZWZfZGYkZmVhdHVyZSA8LSByb3duYW1lcyhjb2VmX2RmKQpjb2xuYW1lcyhjb2VmX2RmKVsxXSA8LSAiY29lZmZpY2llbnQiCgojIEZpbHRlciBub24temVybyBjb2VmZmljaWVudHMKbm9uemVyb19jb2VmcyA8LSBzdWJzZXQoY29lZl9kZiwgY29lZmZpY2llbnQgIT0gMCkKCiMgU2VwYXJhdGUgaW50ZXJjZXB0CmludGVyY2VwdCA8LSByb3VuZChub256ZXJvX2NvZWZzJGNvZWZmaWNpZW50W25vbnplcm9fY29lZnMkZmVhdHVyZSA9PSAiKEludGVyY2VwdCkiXSwgNCkKbm9uemVyb190ZXJtcyA8LSBzdWJzZXQobm9uemVyb19jb2VmcywgZmVhdHVyZSAhPSAiKEludGVyY2VwdCkiKQoKIyBCdWlsZCBtb2RlbCBlcXVhdGlvbiBzdHJpbmcKZXF1YXRpb24gPC0gcGFzdGUwKHJvdW5kKG5vbnplcm9fdGVybXMkY29lZmZpY2llbnQsIDQpLCAiKiIsIG5vbnplcm9fdGVybXMkZmVhdHVyZSkKY2F0KCJNb2RlbCBlcXVhdGlvbjogeSA9IiwgaW50ZXJjZXB0LCAiKyIsIHBhc3RlKGVxdWF0aW9uLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikKCgoKYGBgCgojIyBFbGFzdGljIE5ldCBSZWdyZXNzaW9uIEVxdWF0aW9uCgpgYGB7cn0KCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgd2l0aCBuYW1lcyBmcm9tIGdsbW5ldApjb2VmX2VsYXN0aWMgPC0gY29lZihjdl9lbGFzdGljX25ldCwgcyA9IGJlc3QuZWxhc3RpYy5uZXQubGFtYmRhKQpjb2VmX2RmIDwtIGFzLmRhdGEuZnJhbWUoYXMubWF0cml4KGNvZWZfZWxhc3RpYykpCmNvZWZfZGYkZmVhdHVyZSA8LSByb3duYW1lcyhjb2VmX2RmKQpjb2xuYW1lcyhjb2VmX2RmKVsxXSA8LSAiY29lZmZpY2llbnQiCgojIEZpbHRlciBub24temVybyBjb2VmZmljaWVudHMKbm9uemVyb19jb2VmcyA8LSBzdWJzZXQoY29lZl9kZiwgY29lZmZpY2llbnQgIT0gMCkKCiMgU2VwYXJhdGUgaW50ZXJjZXB0CmludGVyY2VwdCA8LSByb3VuZChub256ZXJvX2NvZWZzJGNvZWZmaWNpZW50W25vbnplcm9fY29lZnMkZmVhdHVyZSA9PSAiKEludGVyY2VwdCkiXSwgNCkKbm9uemVyb190ZXJtcyA8LSBzdWJzZXQobm9uemVyb19jb2VmcywgZmVhdHVyZSAhPSAiKEludGVyY2VwdCkiKQoKIyBCdWlsZCBtb2RlbCBlcXVhdGlvbiBzdHJpbmcKZXF1YXRpb24gPC0gcGFzdGUwKHJvdW5kKG5vbnplcm9fdGVybXMkY29lZmZpY2llbnQsIDQpLCAiKiIsIG5vbnplcm9fdGVybXMkZmVhdHVyZSkKY2F0KCJNb2RlbCBlcXVhdGlvbjogeSA9IiwgaW50ZXJjZXB0LCAiKyIsIHBhc3RlKGVxdWF0aW9uLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikKCgoKYGBgCgojIENvbXBhcmluZyBMaW5lYXIgUmVncmVzc2lvbiB3aXRoIFNWUgoKYGBge3J9CgpjeWJlciA8LSByZWFkLmNzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0dhYmJ5SzgvRGF0YXNldHMvcmVmcy9oZWFkcy9tYWluL0dsb2JhbF9DeWJlcnNlY3VyaXR5X1RocmVhdHNfMjAxNS0yMDI0LmNzdicpCgojT25lLUhvdCBFbmNvZGluZwpkdW1teSA8LSBkdW1teVZhcnMoIn4uIiwgZGF0YT1jeWJlcikKY3liZXIyPC1kYXRhLmZyYW1lKHByZWRpY3QoZHVtbXksIG5ld2RhdGE9Y3liZXIpKQoKIyMjIyMKIyBTcGxpdCBkYXRhIGludG8gZmVhdHVyZXMgKFgpIGFuZCB0YXJnZXQgKHkpClggPC0gY3liZXIyWywgLTI1XSAgIyBBbGwgY29sdW1ucyBleGNlcHQgdGhlIHRhcmdldCB2YXJpYWJsZQp5IDwtIGN5YmVyMlssIDI1XSAgICMgVGFyZ2V0IHZhcmlhYmxlIChGaW5hbmNpYWwgTG9zcyBpbiBNaWxsaW9ucykKCiMgY3JlYXRlIGR1bW15IHZhcmlhYmxlcwoKCgojIyMjIwojIFNwbGl0IGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzCnNldC5zZWVkKDEyMykKdHJhaW4uaW5kZXggPC0gc2FtcGxlKDE6bnJvdyhjeWJlcjIpLCAwLjggKiBucm93KGN5YmVyMikpClgudHJhaW4gPC0gWFt0cmFpbi5pbmRleCwgXQp5LnRyYWluIDwtIHlbdHJhaW4uaW5kZXhdClgudGVzdCA8LSBYWy10cmFpbi5pbmRleCwgXQp5LnRlc3QgPC0geVstdHJhaW4uaW5kZXhdCgojIyMjIwpjeWJlcjIudHJhaW4gPC0gYXMuZGF0YS5mcmFtZShjeWJlcjJbdHJhaW4uaW5kZXgsIF0pCmN5YmVyMi50ZXN0IDwtIGN5YmVyMlstdHJhaW4uaW5kZXgsIF0KIyMjIyMKIyMgU2V0IHVwIGN1c3RvbSBjcm9zcy12YWxpZGF0aW9uIGNvbnRyb2wKbXlfdHVuZV9jb250cm9sIDwtIHR1bmUuY29udHJvbCgKICBjcm9zcyA9IDUsICAjIFVzZSA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiwgdGhlIGRlZmF1bHQgaXMgMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uCiAgbnJlcGVhdCA9IDEgIyBOdW1iZXIgb2YgcmVwZXRpdGlvbnMgKGZvciByZXBlYXRlZCBjcm9zcy12YWxpZGF0aW9uKQopCiMjIyMjCiMgUGVyZm9ybSBncmlkIHNlYXJjaCBmb3IgaHlwZXJwYXJhbWV0ZXIgdHVuaW5nOiBSQkYga2VybmVsIGlzIHVzZWQKdHVuZS5SQkYgPC0gdHVuZShzdm0sCiAgICAgICAgICAgICAgICAgdHJhaW4ueCA9IFgudHJhaW4sIAogICAgICAgICAgICAgICAgIHRyYWluLnkgPSB5LnRyYWluLCAKICAgICAgICAgICAgICAgICAgICByYW5nZXMgPSBsaXN0KGVwc2lsb24gPSBzZXEoMC4xLCAwLjUsIDAuMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29zdCA9IGMoMSwgMTAsIDEwMCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2FtbWEgPSBjKDAuMDEsIDAuMSwgMSkpLCAjIEh5cGVycGFyIGluIFJCRgogICAgICAgICAgICAgICAgICAgIHR1bmVjb250cm9sID0gbXlfdHVuZV9jb250cm9sICAgICMgNS1mb2xkIGNyb3NzLXZhbGlkYXRpb24KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCiMjIyMKIyBEaXNwbGF5IHRoZSBiZXN0IHBhcmFtZXRlcnMKI3ByaW50KHR1bmUucmVzdWx0cyRiZXN0LnBhcmFtZXRlcnMpCiMjIyMjCiMgVHJhaW4gdGhlIGZpbmFsIG1vZGVsIHVzaW5nIHRoZSBiZXN0IHBhcmFtZXRlcnMKZmluYWwuUkJGPC0gc3ZtKFgudHJhaW4sIHkudHJhaW4sIAogICAgICAgICAgICAgICAgICAgdHlwZSA9ICJlcHMtcmVncmVzc2lvbiIsICAjIFVzZSAibnUtcmVncmVzc2lvbiIgZm9yIG51LVNWUgogICAgICAgICAgICAgICAgICAga2VybmVsID0gInJhZGlhbCIsIAogICAgICAgICAgICAgICAgICAgZXBzaWxvbiA9IHR1bmUuUkJGJGJlc3QucGFyYW1ldGVycyRlcHNpbG9uLCAKICAgICAgICAgICAgICAgICAgIGNvc3QgPSB0dW5lLlJCRiRiZXN0LnBhcmFtZXRlcnMkY29zdCwgCiAgICAgICAgICAgICAgICAgICBnYW1tYSA9IHR1bmUuUkJGJGJlc3QucGFyYW1ldGVycyRnYW1tYSkKIyMjIyMKIyBNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldApwcmVkLlJCRiA8LSBwcmVkaWN0KGZpbmFsLlJCRiwgWC50ZXN0KQoKIyBFdmFsdWF0ZSBwZXJmb3JtYW5jZQptc2UuUkJGIDwtIG1lYW4oKHkudGVzdCAtIHByZWQuUkJGKV4yKSAgICAjIG1lYW4gc3F1YXJlIGVycm9yCm1hZS5SQkYgPC0gbWVhbihhYnMoeS50ZXN0IC0gcHJlZC5SQkYpKSAgICMgbWVhbiBhYnNvbHV0ZSBlcnJvcgoKIyMjIyBsaW5lYXIga2VybmVsCgojIFBlcmZvcm0gZ3JpZCBzZWFyY2ggZm9yIGh5cGVycGFyYW1ldGVyIHR1bmluZzogUkJGIGtlcm5lbCBpcyB1c2VkCnR1bmUubGluIDwtIHR1bmUoc3ZtLCB0cmFpbi54ID0gWC50cmFpbiwgdHJhaW4ueSA9IHkudHJhaW4sIAogICAgICAgICAgICAgICAgICAgIHJhbmdlcyA9IGxpc3QoZXBzaWxvbiA9IHNlcSgwLjEsIDAuNSwgMC4xKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3N0ID0gYygxLCAxMCwgMTAwKSksIAogICAgICAgICAgICAgICAgICAgIHR1bmVjb250cm9sID0gbXlfdHVuZV9jb250cm9sICMgNS1mb2xkIGNyb3NzLXZhbGlkYXRpb24KICAgICAgICAgICAgICAgICAgICApCiMjIyMKIyBEaXNwbGF5IHRoZSBiZXN0IHBhcmFtZXRlcnMKI3ByaW50KHR1bmUubGluJGJlc3QucGFyYW1ldGVycykKIyMjIyMKIyBUcmFpbiB0aGUgZmluYWwgbW9kZWwgdXNpbmcgdGhlIGJlc3QgcGFyYW1ldGVycwpmaW5hbC5saW48LSBzdm0oWC50cmFpbiwgeS50cmFpbiwgCiAgICAgICAgICAgICAgICAgICB0eXBlID0gImVwcy1yZWdyZXNzaW9uIiwgICMgVXNlICJudS1yZWdyZXNzaW9uIiBmb3IgbnUtU1ZSCiAgICAgICAgICAgICAgICAgICBrZXJuZWwgPSAibGluZWFyIiwgCiAgICAgICAgICAgICAgICAgICBlcHNpbG9uID0gdHVuZS5saW4kYmVzdC5wYXJhbWV0ZXJzJGVwc2lsb24sIAogICAgICAgICAgICAgICAgICAgY29zdCA9IHR1bmUubGluJGJlc3QucGFyYW1ldGVycyRjb3N0KQojIyMjIwojIE1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0CnByZWQubGluIDwtIHByZWRpY3QoZmluYWwubGluLCBYLnRlc3QpCgojIEV2YWx1YXRlIHBlcmZvcm1hbmNlCm1zZS5saW4gPC0gbWVhbigoeS50ZXN0IC0gcHJlZC5saW4pXjIpICAgICMgbWVhbiBzcXVhcmUgZXJyb3IKbWFlLmxpbiA8LSBtZWFuKGFicyh5LnRlc3QgLSBwcmVkLmxpbikpCgojIEFmdGVyIHR1bmluZyBpcyBjb21wbGV0ZQoKCgpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhlIHBsb3RzIGFyZSBhIGRpYWdub3N0aWMgcGxvdCBmb3IgdGhlIHJlZ3Jlc3Npb24gbW9kZWwuJ30KCiMjIG9yZGluYXJ5IExTRSByZWdyZXNzaW9uIG1vZGVsIHdpdGggc3RlcHdpc2UgdmFyaWFibGUgc2VsZWN0aW9uCmxzZS5maXQgPC0gbG0oRmluYW5jaWFsLkxvc3MuLmluLk1pbGxpb24uLi5+LixkYXRhPWN5YmVyMi50cmFpbikKQUlDLmZpdCA8LSBzdGVwQUlDKGxzZS5maXQsZGlyZWN0aW9uPSJib3RoIiwgdHJhY2UgPSBGQUxTRSkKcHJlZC5sc2UgPC0gcHJlZGljdChBSUMuZml0LCBYLnRlc3QpCm1zZS5sc2UgPC0gbWVhbigoeS50ZXN0IC0gcHJlZC5sc2UpXjIpICAgICMgbWVhbiBzcXVhcmUgZXJyb3IKbWFlLmxzZSA8LSBtZWFuKGFicyh5LnRlc3QgLSBwcmVkLmxzZSkpICAgIyBtZWFuIGFic29sdXRlIGVycm9yCiMjIwpwYXIobWZyb3c9YygyLDIpLCBtYXI9YygyLDIsMiwyKSkKcGxvdChBSUMuZml0KQoKCmBgYAoKYGBge3J9CiMjIwpQZXJmb3JtYW5jZSA8LSBkYXRhLmZyYW1lKFJCRi5TVlI9Yyhtc2UuUkJGLCBtYWUuUkJGKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBMaW5lYXIuU1ZSID0gYyhtc2UubGluLCBtYWUubGluKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBMU0UuUmVnID1jKG1zZS5sc2UsIG1hZS5sc2UpKQpyb3cubmFtZXMoUGVyZm9ybWFuY2UpIDwtIGMoIk1TRSIsICJNQUUiKQojIwpwYW5kZXIoUGVyZm9ybWFuY2UpCmBgYAoKQmFzZWQgb24gb3VyIE1TRSBhbmQgTUFFIHNjb3Jlcywgb3VyIGxlYXN0IHNxdWFyZXMgZXN0aW1hdGlvbiBoYXMgdGhlIGJlc3QgbW9kZWwuIEl0IGhhcyBsb3dlciBNU0UgYW5kIE1BRSBzY29yZXMgaW5kaWNhdGluZyBhIGJldHRlciBmaXQgbW9kZWwuCg==